package com.chahua.organized.service.impl;

import com.chahua.organized.constant.CommonConstant;
import com.chahua.organized.dao.DepartmentMemberDao;
import com.chahua.organized.entity.Department;
import com.chahua.organized.dao.DepartmentDao;
import com.chahua.organized.entity.DepartmentMember;
import com.chahua.organized.exception.DepartmentException;
import com.chahua.organized.qo.DepartmentQo;
import com.chahua.organized.service.DepartmentService;
import com.chahua.organized.vo.DepartmentInfoVo;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;


@Service("departmentService")
public class DepartmentServiceImpl implements DepartmentService, CommonConstant {

    @Resource
    private DepartmentDao departmentDao;

    @Resource
    private DepartmentMemberDao departmentMemberDao;

    /**
     * 新增内置部门
     */
    @PostConstruct
    public void init() {
        // 判断表中的数据是否为空，如果为空，说明根节点不存在，初始化根节点
        List<Department> departmentList = departmentDao.findAll();
        if (CollectionUtils.isEmpty(departmentList)) {
            // 添加系统内置部门
            Department rootDept = new Department(BUILT_IN_ROOT_DEPARTMENT_NAME, BUILT_IN_ROOT_DEPARTMENT_PARENT_ID);
            departmentDao.insertDepartment(rootDept);
            Department department = new Department(BUILT_IN_DEPARTMENT_NAME, rootDept.getId());
            departmentDao.insertDepartment(department);
        }
    }

    /**
     * 获取部门信息
     * @param id 部门id
     * @return Department
     */
    @Override
    public DepartmentInfoVo getById(int id) {
        Department department = departmentDao.findById(id);
        DepartmentInfoVo departmentInfoVo = new DepartmentInfoVo();
        // 设置部门id，name
        BeanUtils.copyProperties(department,departmentInfoVo);
        // 设置当前部门的父级节点路径deptPath
        List<Integer> deptPathIds = getDepartmentPath(id);
        List<String> deptPath = deptPathIds.stream().map(deptPathId -> {
            return departmentDao.findById(deptPathId).getName();
        }).collect(Collectors.toList());
        departmentInfoVo.setDeptPath(deptPath);
        return departmentInfoVo;
    }

    /**
     * 新增部门
     * @param departmentQo 数据
     */
    @Override
    public void add(DepartmentQo departmentQo) {
        // 1、如果父级部门不存在，无法新增
        checkDepartmentIsExist(departmentQo.getPid());
        // 2、同一个父级部门下的子部门名称不可重复
        checkNameIsRepeatable(departmentQo.getPid(),departmentQo.getName());
        // 3、节点层级最多为10级
        checkLevelIsOverLimit(departmentQo.getPid());
        Department departmentInsert = new Department(departmentQo.getName(), departmentQo.getPid());
        departmentDao.insertDepartment(departmentInsert);
    }


    /**
     * 更新部门
     * @param id           部门id
     * @param departmentQo 数据
     */
    @Override
    public void updateById(int id, DepartmentQo departmentQo) {
        // 1、校验更新部门是否存在
        checkDepartmentIsExist(id);
        // 2、校验父级部门是否存在
        checkDepartmentIsExist(departmentQo.getPid());
        // 3、同一个父级部门下的子部门名称不可重复
        checkNameIsRepeatable(departmentQo.getPid(),departmentQo.getName());
        // 4、节点层级最多为10级
        checkLevelIsOverLimit(departmentQo.getPid());
        // 更新部门
        Department department = departmentDao.findById(id);
        department.setPid(departmentQo.getPid());
        department.setName(departmentQo.getName());
        department.setUpdateTime(new Date());
        departmentDao.updateDepartment(department);
    }

    /**
     * 删除部门
     * @param id 部门id
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delete(Integer id) {
        // 校验待删除部门是否存在
        Department department = departmentDao.findById(id);
        if (Objects.isNull(department)) {
            throw new DepartmentException("department.is.not.exist");
        }
        // 获取待删除的部门及其所有子部门
        List<Department> departmentList = departmentDao.findAll();
        List<Integer> deleteIds = getChildrenIds(department, departmentList, new ArrayList<>());
        deleteIds.add(department.getId());
        // 将所有删除部门的成员移动到默认部门下
        List<DepartmentMember> departmentMemberList = departmentMemberDao.findByDeptIds(deleteIds);
        if(!CollectionUtils.isEmpty(departmentMemberList)){
            List<Integer> deleteMemberIds = departmentMemberList.stream().map(DepartmentMember::getId).collect(Collectors.toList());
            // 根据根节点获取默认部门id
            Integer rootDeptId = departmentDao.findIdByPidAndName(-1,"部门");
            Integer defaultDeptId = departmentDao.findIdByPidAndName(rootDeptId, "默认部门");
            departmentMemberDao.updateByIds(deleteMemberIds,defaultDeptId);
        }
        // 批量删除部门
        departmentDao.delete(deleteIds);
    }

    /**
     * 递归获取当前部门的所有子部门id
     * @param root 当前删除部门
     * @param all  所有部门
     * @param path 待删除部门id
     * @return List<Integer>
     */
    private List<Integer> getChildrenIds(Department root, List<Department> all, List<Integer> path) {
        // 获取一级子部门
        List<Department> departmentList = all.stream().filter(department -> {
            return department.getPid().equals(root.getId());
        }).collect(Collectors.toList());

        if(CollectionUtils.isEmpty(departmentList)){
            return path;
        }
        for(Department department:departmentList){
            path.add(department.getId());
            getChildrenIds(department,all,path);
        }
        return path;
    }

    /**
     * 获取部门树
     * @return List<Department>
     */
    @Override
    public List<Department> listWithTree() {
        // 1、查询所有部门
        List<Department> departmentList = departmentDao.findAll();
        if (CollectionUtils.isEmpty(departmentList)) {
            return departmentList;
        }
        // 2、组成树形结构
        // 找到所有的一级分类
        return departmentList.stream().filter(department -> {
            return department.getPid() == -1;
        }).map(menu -> {
            // 设置每个部门的子部门
            menu.setChildren(getChildrens(menu, departmentList));
            return menu;
        }).collect(Collectors.toList());
    }

    /**
     * 递归获取当前部门的所有子部门
     * @param root           当前部门
     * @param departmentList 所有部门
     * @return 当前部门的子部门
     */
    private List<Department> getChildrens(Department root, List<Department> departmentList) {
        return departmentList.stream().filter(department -> {
            return department.getPid().equals(root.getId());
        }).map(department -> {
            department.setChildren(getChildrens(department, departmentList));
            return department;
        }).collect(Collectors.toList());
    }

    /**
     * 递归获取父级节点层级数
     * @param pid 父级节点id
     * @return 父级节点层级数
     */
    private List<Integer> getPathCount( List<Integer> pathCount,int pid) {
        // 当前节点的id
        pathCount.add(pid);
        // 如果当前节点还有父级节点，将父级节点的id加上去
        Department department = departmentDao.findById(pid);
        if(department.getPid()!=-1){
            getPathCount(pathCount,department.getPid());
        }
        return pathCount;
    }

    /**
     * 根据当前部门的id，获取部门树的完整路径:[1,2,3]
     * @param id 部门id
     * @return 部门节点路径
     */
    @Override
    public List<Integer> getDepartmentPath(int id) {
        List<Integer> list = new ArrayList<>();
        List<Integer> parentPath = findParentPath(id, list);
        Collections.reverse(parentPath);
        return parentPath;
    }

    /**
     * 递归查询出该部门的所有父级部门: [3,2,1]
     * @param id   部门id
     * @param path 部门节点路径
     * @return List<Long>
     */
    private List<Integer> findParentPath(int id, List<Integer> path) {
        Department department = departmentDao.findById(id);
        // 将当前节点添加剂path
        path.add(id);
        // 如果当前节点还有父节点，将父节点添加到path
        if (department.getPid() != -1) {
            findParentPath(department.getPid(), path);
        }
        return path;
    }


    /**
     * 校验节点层级最多为10级
     * @param pid 父级部门id
     */
    private void checkLevelIsOverLimit(Integer pid) {
        List<Integer> pathCount = new ArrayList<>();
        int levelCount =  getPathCount(pathCount,pid).size()+1;
        if(levelCount>10){
            throw new DepartmentException("the.maximum.level.of.department.node.is.10");
        }
    }

    /**
     * 校验部门名称是否重复
     * @param pid  父级部门id
     * @param name 更新后的部门名称
     */
    private void checkNameIsRepeatable(Integer pid, String name) {
        int count = departmentDao.findByPidAndName(pid,name);
        if(count>0){
            throw new DepartmentException("department.name.is.not.allow.repeatable");
        }
    }

    /**
     * 校验部门是否存在
     * @param id 部门id
     */
    private void checkDepartmentIsExist(Integer id) {
        Department department = departmentDao.findById(id);
        if (Objects.isNull(department)) {
            throw new DepartmentException("department.is.not.exist");
        }
    }

}
